package io.gitee.loulan_yxq.owner.http;

import io.gitee.loulan_yxq.owner.core.exception.IORuntimeException;
import io.gitee.loulan_yxq.owner.core.map.MapTool;
import io.gitee.loulan_yxq.owner.core.tool.CharsetTool;
import io.gitee.loulan_yxq.owner.core.tool.ObjectTool;
import io.gitee.loulan_yxq.owner.core.tool.StrTool;
import io.gitee.loulan_yxq.owner.http.exception.URISyntaxRuntimeException;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import java.io.IOException;
import java.io.StreamTokenizer;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.Map;

/*********************************************************
 ** http请求对象
 ** <br><br>
 ** Date: Created in 2021/11/1  16:10
 ** @author loulan
 ** @version 0.0.0
 *********************************************************/
public class HttpRequest extends HttpEntityEnclosingRequestBase {
    // http的客户端对象
    private CloseableHttpClient httpClient;
    // uri对象的创建对象
    private URIBuilder uriBuilder;
    // 请求方法
    private HttpMethod httpMethod;

    private HttpRequest(Builder builder) {
        this.httpClient = builder.httpClient;
        this.uriBuilder = builder.uriBuilder;
        this.httpMethod = builder.httpMethod;
        // 设置请求头
        if (MapTool.isNotEmpty(builder.headers)) {
            builder.headers.forEach((key, value) -> {
                super.setHeader(key, String.valueOf(value));
            });
        }

        // 设置请求体
        if (ObjectTool.isNotNull(builder.entityBuilder)) {
            super.setEntity(builder.entityBuilder.build());
        }
    }

    /**
     * 运行执行当前的请求
     *
     * @return 请求之后的响应对象
     * @author :loulan
     */
    public HttpResponse execute() {
        try {
            /**************************************************/
            this.setURI(uriBuilder.build());
            /**************************************************/
            // 执行请求
            CloseableHttpResponse execute = httpClient.execute(this);
            return new HttpResponse.Builder().httpResponse(execute).httpClient(httpClient).build();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } catch (URISyntaxException e) {
            throw new URISyntaxRuntimeException(e);
        }
    }

    /**
     * 实现{@link HttpRequestBase}需要实现的方法
     *
     * @return 方法名称
     * @author :loulan
     */
    @Override
    public String getMethod() {
        return httpMethod.toString();
    }

    /*********************************************************
     ** {@link HttpRequest}的Builder对象
     ** <br><br>
     ** Date: Created in 2021/11/12 9:15
     ** @author loulan
     ** @version 0.0.0
     *********************************************************/
    public static class Builder {
        // http的客户端对象
        private CloseableHttpClient httpClient;
        // uri对象的创建对象
        private URIBuilder uriBuilder;
        // 请求方法
        private HttpMethod httpMethod;
        // 编码方式
        private Charset charset = CharsetTool.defaultCharset();
        // 请求头配置数据
        private Map<String, Object> headers;
        // 请求体构建对象
        private EntityBuilder entityBuilder;

        private Builder() {
            this.httpClient = HttpClients.createDefault();
        }


        /**
         * 设置方法，
         *
         * @param httpMethod 方法枚举类型
         * @return {@link Builder}对象
         * @author :loulan
         */
        public Builder method(HttpMethod httpMethod) {
            this.httpMethod = httpMethod;
            return this;
        }

        /**
         * 设置请求对象的请求头
         *
         * @param headers 请求头集合对象
         * @return {@link Builder}对象
         * @author :loulan
         */
        public Builder headers(Map<String, ? extends Object> headers) {
            // 判断参数是否为空
            if (MapTool.isNotEmpty(headers)) {
                if (MapTool.isEmpty(this.headers)) {
                    this.headers = MapTool.map();
                }

                this.headers.putAll(headers);
            }
            return this;
        }

        /**
         * 设置请求对象的请求头
         *
         * @param key   请求头的类型
         * @param value 请求头的值
         * @return {@link Builder}对象
         * @author :loulan
         */
        public Builder header(String key, Object value) {
            if (MapTool.isEmpty(headers)) {
                this.headers = MapTool.map();
            }
            this.headers.put(key, value);
            return this;
        }

        /**
         * 设置请求对象的请求头
         *
         * @param bodyJson 请求体的json数据
         * @return {@link Builder}对象
         * @author :loulan
         */
        public Builder body(String bodyJson) {
            if (StrTool.isNotEmpty(bodyJson)) {
                if (ObjectTool.isNull(this.entityBuilder)) {
                    this.entityBuilder = EntityBuilder.create();
                }
                this.entityBuilder.setText(bodyJson).setContentType(ContentType.APPLICATION_JSON);
            }
            return this;
        }

        /**
         * 设置编码方式
         *
         * @param charset 编码方式
         * @return {@link Builder}对象
         * @author :loulan
         */
        public Builder charset(Charset charset) {
            this.charset = ObjectTool.isNull(charset) ? CharsetTool.defaultCharset() : charset;
            return this;
        }

        /**
         * Description :设置请求链接的参数
         *
         * @param key   参数的key
         * @param value 参数的值
         * @return {@link Builder}对象
         * @author :loulan
         */
        public Builder param(String key, Object value) {
            this.uriBuilder.setParameter(key, String.valueOf(value));
            return this;
        }

        /**
         * Description :设置请求链接的参数
         *
         * @param paramMap 参数集合
         * @return {@link Builder}对象
         * @author :loulan
         */
        public Builder param(Map<String, ? extends Object> paramMap) {
            if (MapTool.isNotEmpty(paramMap)) {
                paramMap.forEach((key, value) -> {
                    this.uriBuilder.setParameter(key, String.valueOf(value));
                });
            }
            return this;
        }

        /**
         * 设置当前对象url地址,内部使用，需要在获取对象的时候就创建
         *
         * @param url 地址链接
         * @return {@link Builder}对象
         * @author :loulan
         */
        private Builder url(String url) {
            try {
                this.uriBuilder = new URIBuilder(url);
            } catch (URISyntaxException e) {
                throw new URISyntaxRuntimeException(e);
            }
            return this;
        }

        /**
         * 创建{@link HttpRequest}对象
         *
         * @return {@link HttpRequest}对象
         * @author :loulan
         */
        public HttpRequest build() {
            return new HttpRequest(this);
        }

        /**
         * 获取get请求并设置url地址
         *
         * @param url 地址链接
         * @return {@link Builder}对象
         * @author :loulan
         */
        public static Builder get(String url) {
            return new Builder().method(HttpMethod.GET).url(url);
        }

        /**
         * 获取post请求并设置url地址
         *
         * @param url 地址链接
         * @return {@link Builder}对象
         * @author :loulan
         */
        public static Builder post(String url) {
            return new Builder().method(HttpMethod.POST).url(url);
        }

        /**
         * 获取head请求并设置url地址
         *
         * @param url 地址链接
         * @return {@link Builder}对象
         * @author :loulan
         */
        public static Builder head(String url) {
            return new Builder().method(HttpMethod.HEAD).url(url);
        }

        /**
         * 获取delete请求并设置url地址
         *
         * @param url 地址链接
         * @return {@link Builder}对象
         * @author :loulan
         */
        public static Builder delete(String url) {
            return new Builder().method(HttpMethod.DELETE).url(url);
        }

        /**
         * 获取options请求并设置url地址
         *
         * @param url 地址链接
         * @return {@link Builder}对象
         * @author :loulan
         */
        public static Builder options(String url) {
            return new Builder().method(HttpMethod.OPTIONS).url(url);
        }

        /**
         * 获取put请求并设置url地址
         *
         * @param url 地址链接
         * @return {@link Builder}对象
         * @author :loulan
         */
        public static Builder put(String url) {
            return new Builder().method(HttpMethod.PUT).url(url);
        }

        /**
         * 获取patch请求并设置url地址
         *
         * @param url 地址链接
         * @return {@link Builder}对象
         * @author :loulan
         */
        public static Builder patch(String url) {
            return new Builder().method(HttpMethod.PATCH).url(url);
        }

        /**
         * 获取trace请求并设置url地址
         *
         * @param url 地址链接
         * @return {@link Builder}对象
         * @author :loulan
         */
        public static Builder trace(String url) {
            return new Builder().method(HttpMethod.TRACE).url(url);
        }

        /**
         * 获取请求并设置url地址（该请求需要后期调用method方法进行设置，默认GET请求方法），
         *
         * @param url 地址链接
         * @return {@link Builder}对象
         * @author :loulan
         */
        public static Builder request(String url) {
            return new Builder().url(url);
        }
    }
}
